package org.flexthinker.binding.bean
{
    import org.flexthinker.binding.value.PropertyChangeListener;
    import org.flexthinker.binding.value.ValueModel;
    import org.flexthinker.binding.value.support.DelegatingPropertyChangeListener;
    import org.flexthinker.binding.value.support.ValueHolder;

    import mx.events.PropertyChangeEvent;

    /**
     * Base class for <code>BeanProperty</code> and <code>ElProperty</code>
     *
     * @author Dragos Dascalita Haut
     */
    public class PropertyHelper implements ValueModel
    {
        private var _source:Object;
        private var _path:String;

        protected var valueModelsInPath:Vector.<ValueModel> = new Vector.<ValueModel>();

        protected var changeListeners:Vector.<PropertyChangeListener> = new Vector.<PropertyChangeListener>();

        public function PropertyHelper(source:Object, path:String):void
        {
            if ( ! path )
            {
                throw new Error("Must provide a path to a property");
            }
            _path = path;

            if ( source )
            {
                setSource( source );
            }
        }

        public function setSource( source:Object ):void
        {
            if ( source == null )
            {
                return;
            }
            _source = source;

        }

        public function getSource():Object
        {
            return _source;
        }

        public function getPath():String
        {
            return _path;
        }

        public function getValue():Object
        {
            throw new Error("getValue() method should have been overridden in the super class.");
            return null;
        }


        public function setValue( value:Object ):void
        {
            throw new Error("setValue() method should have been overridden in the super class.")
        }


        public function addValueChangeListener( listener:PropertyChangeListener ):void
        {
            changeListeners.push( listener );
        }


        public function removeValueChangeListener( listener:PropertyChangeListener ):void
        {
            var idx:int = changeListeners.indexOf( listener );
            if ( idx >= 0 )
            {
                changeListeners.splice(idx, 1);
            }
        }


        public function removeAllListener():void
        {
            changeListeners = new Vector.<PropertyChangeListener>();
        }


        public function equals( obj:Object ):Boolean
        {
            throw new Error("Not allowed to call equals");
        }

        // --------------------
        //   internal methods
        // ---------------------


        protected function resetWatchers():void
        {
            for each ( var vm:ValueModel in valueModelsInPath )
            {
                vm.removeAllListener();
            }
            valueModelsInPath = new Vector.<ValueModel>();
        }

        protected function createValueModel( subpathHost:Object, subpathPropertyName:String ):ValueModel
        {
            var vh:ValueHolder = new ValueHolder(subpathHost, subpathPropertyName);
            valueModelsInPath.push(vh);
            return vh;
        }

        protected function listenTo( vm:ValueModel ):void
        {
            vm.addValueChangeListener( new DelegatingPropertyChangeListener( subPropertyChangedHandler ) );
        }

        protected function subPropertyChangedHandler( event:PropertyChangeEvent ):void
        {
             notifyAllListeners(event);
        }

        private function notifyAllListeners(event:PropertyChangeEvent):void
        {
            for each ( var listener:PropertyChangeListener in changeListeners )
            {
                listener.propertyChange( event );
            }
        }

    }
}